package com.my.framework.shiro;

import com.google.common.collect.Sets;
import com.my.framework.utils.Encodes;
import com.my.xadmin.security.entity.*;
import com.my.xadmin.security.service.ModuleService;
import com.my.xadmin.security.service.OrganizationRoleService;
import com.my.xadmin.security.service.UserRoleService;
import com.my.xadmin.security.service.UserService;
import com.octo.captcha.service.image.ImageCaptchaService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import sun.security.util.Password;

import java.util.Collection;
import java.util.List;
import java.util.Set;


public class ShiroDbRealm extends AuthorizingRealm {
	private static final Logger logger = LoggerFactory.getLogger(ShiroDbRealm.class);

	// 是否启用超级管理员
	protected boolean activeRoot = false;
	// 是否使用验证码
	protected boolean useCaptcha = false;

	@Autowired
	private UserService userService;

	@Autowired
	private UserRoleService userRoleService;

	@Autowired
	private OrganizationRoleService organizationRoleService;

	@Autowired
	private ModuleService moduleService;

	protected ImageCaptchaService imageCaptchaService;

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		//1.是否启动验证码. 把 AuthenticationToken 转换为 自定义CaptchaUsernamePasswordToken
		if (useCaptcha)
		{
			CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken;
			String parm = token.getCaptcha();
			try
			{
				if (!imageCaptchaService.validateResponseForID(SecurityUtils.getSubject().getSession().getId().toString(), parm.toLowerCase()))
				{//忽略大小写。
					throw new IncorrectCaptchaException("验证码错误！");
				}
			}
			catch (Exception e)
			{
				// session如果没有刷新，validateResponseForID会抛出com.octo.captcha.service.CaptchaServiceException的异常
				throw new IncorrectCaptchaException("验证码错误！");
			}
		}
		//1.把 AuthenticationToken 转换为 UsernamePasswordToken
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

		//2. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
		User user = userService.getByUsername(token.getUsername());
		if (user != null)
		{
			if (user.getStatus().equals("disabled"))
			{
				throw new LockedAccountException("用户被锁定");
			}

			byte[] salt = Encodes.decodeHex(user.getSalt());

			ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUsername(), user);
			// 这里可以缓存认证
			return new SimpleAuthenticationInfo(shiroUser, user.getPassword(), ByteSource.Util.bytes(salt), getName());
		} else {
			throw new UnknownAccountException("账号或者密码错误");
		}

	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		Collection<?> collection = principals.fromRealm(getName());
		if (CollectionUtils.isEmpty(collection)) {
			return null;
		}
		ShiroUser shiroUser = (ShiroUser) collection.iterator().next();

		List<UserRole> userRoles = userRoleService.find(shiroUser.getId());
		List<OrganizationRole> organizationRoles = organizationRoleService.find(shiroUser.getUser().getOrganization().getId());

		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.addStringPermissions(makePermissions(userRoles, organizationRoles, shiroUser));

		return info;
	}

	private Collection<String> makePermissions(List<UserRole> userRoles, List<OrganizationRole> organizationRoles, ShiroUser shiroUser) {
		// 是否启用超级管理员
		if (activeRoot) {
			// 为超级管理员，构造所有权限
			if (userService.isSupervisor(shiroUser.getId())) {
				Collection<String> stringPermissions = Sets.newHashSet();

				List<Module> modules = moduleService.findAll();
				for (Module module : modules) {
					List<Permission> permissions = module.getPermissions();
					// 默认构造CRUD权限
					stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_CREATE);
					stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_READ);
					stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_UPDATE);
					stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_DELETE);

					for (Permission permission : permissions) {
						stringPermissions.add(module.getSn() + ":" + permission.getShortName());
					}
				}
				return stringPermissions;
			}
		}

		Set<Role> roles = Sets.newHashSet();
		for (UserRole userRole : userRoles) {
			roles.add(userRole.getRole());
		}

		for (OrganizationRole organizationRole : organizationRoles) {
			roles.add(organizationRole.getRole());
		}

		Collection<String> stringPermissions = Sets.newHashSet();
		for (Role role : roles) {
			List<RolePermission> rolePermissions = role.getRolePermissions();
			for (RolePermission rolePermission : rolePermissions) {
				Permission permission = rolePermission.getPermission();
				stringPermissions.add(permission.getModule().getSn() + ":" + permission.getShortName());
			}
		}

//		log.info(shiroUser.getLoginName() + "拥有的权限:" + stringPermissions);
		return stringPermissions;
	}

	/**
	 * 设置 isActiveRoot 的值
	 */
	public void setActiveRoot(boolean activeRoot) {
		this.activeRoot = activeRoot;
	}

	/**
	 * 清除所有用户授权信息缓存.
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
		clearCachedAuthorizationInfo(principals);
	}

}